#ifndef _CACHE_
#define _CACHE_

#include <inttypes.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <errno.h>

#include "log.h"
#include "ursax.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef void (*foo_destructor)(void* data);

extern void foo_destructor_noop(void* data);

typedef int (*iteration_callback)(uint64_t key, void* data, void* arg);
typedef int (*iteration_callback_reference)(uint64_t key, void* data, void* arg, int reference);


/************************************* hash table ***************************************/

struct foo_hashtable;

// create a hash table instance, optionally providing a dtor of hash table item
// the default dtor is free(void*)
// returns the pointer to the instance (always sucessful)
struct foo_hashtable* foo_hashtable_create(foo_destructor free_cb);

// insert a key and item pair into the hash table
// returns 0 for success, -EINVAL for failure (key is found in hash table)
int foo_hashtable_put(struct foo_hashtable* ht, uint64_t key, void* data);

// look for a key in the hash table
// returns 0 for not found, 1 for found
int foo_hashtable_lookup(struct foo_hashtable* ht, uint64_t key);

// get the corresponding item of a key
// returns 0 for success, -EINVAL for failure (key not found)
int foo_hashtable_get(struct foo_hashtable* ht, uint64_t key, void* result);

// return the count of items in the hash table
unsigned int foo_hashtable_entry_count(struct foo_hashtable* ht);

// remove a key from the hash table, either get the corresponding item by *result, or destruct it 
// returns 0 for success, -EINVAL for failure (key not found)
int foo_hashtable_remove(struct foo_hashtable* ht, uint64_t key, void* result);

// iterate the items in the hash table through callback function
// the iteration is terminated if the callback returns negative int
// returns 0 for success, or the value that callback() returns when terminated
int foo_hashtable_iterate(struct foo_hashtable* ht, iteration_callback callback, void* arg);

// delete the hash table, optionally and the items in it
// returns 0 for success (always)
int foo_hashtable_delete(struct foo_hashtable* ht, int keep_data);



/************************************* hash set ***************************************/

struct foo_hashset;

static inline struct foo_hashset* foo_hashset_create()
{
	return (struct foo_hashset*)foo_hashtable_create(&foo_destructor_noop);
}

static inline int foo_hashset_put(struct foo_hashset* hash, uint64_t key)
{
	return foo_hashtable_put((struct foo_hashtable*)hash, key, NULL);
}

static inline int foo_hashset_lookup(struct foo_hashset* hash, uint64_t key)
{
	return foo_hashtable_lookup((struct foo_hashtable*)hash, key);
}

static inline int foo_hashset_remove(struct foo_hashset* hash, uint64_t key)
{
	return foo_hashtable_remove((struct foo_hashtable*)hash, key, NULL);
}

static inline unsigned int foo_hashset_entry_count(struct foo_hashset* hash)
{
	return foo_hashtable_entry_count((struct foo_hashtable*)hash);
}

static inline int foo_hashset_iterate(struct foo_hashset* hash, iteration_callback callback, void* arg)
{
	return foo_hashtable_iterate((struct foo_hashtable*)hash, callback, arg);
}

static inline int foo_hashset_delete(struct foo_hashset* hash)
{
	return foo_hashtable_delete((struct foo_hashtable*)hash, 0);
}


/************************************* LRU Cache ***************************************/

struct foo_cache;

// create a cache instance, optionally providing a dtor of cache item
// the default dtor is free(void*)
// if auto_compaction is true, foo_cache_releae_any() will take it into consideration for releasing
// returns the pointer to the instance (always sucessful)
struct foo_cache* foo_cache_create(size_t capacity, foo_destructor free_cb, bool auto_compaction);

// delete the cache, optionally and the items in it
// returns 0 for success (always)
int foo_cache_delete(struct foo_cache* cache, int keep_data);

// look for a key in the cache, and optionally get the corresponding item, if found
// returns 0 for not found, 1 for found
int foo_cache_lookup(struct foo_cache* cache, uint64_t key);

// get the corresponding item of a key, and remove it from the cache
// returns 0 for success, -EINVAL for failure (key not found)
int foo_cache_get(struct foo_cache* cache, uint64_t key, void* result);

// get the corresponding item of a key, and mark it as most recently used, and increase its number of reference
// returns 0 for success, -EINVAL for failure (key not found)
int foo_cache_get_reference(struct foo_cache* cache, uint64_t key, void* result);

// adjust the number of reference
// returns the number of reference after the adjust, -EINVAL for failure (key not found)
int foo_cache_adjust_reference(struct foo_cache* cache, uint64_t key, int16_t delta);

// remove a key from the cache, either get the corresponding item by *result, or destruct it 
// returns 0 for success, -EINVAL for failure (key not found)
int foo_cache_remove(struct foo_cache* cache, uint64_t key, void* result);

// insert a key and item pair into the cache, and remove an old item if cache is full
// returns 0 for success, -EINVAL for failure (key is found in cache)
int foo_cache_put(struct foo_cache* cache, uint64_t key, void* data);

// if (the key is found in cache) {entry->reference--;}
// else {insert a key and item pair into the cache, with its initial reference;}
// return entry->reference;
int foo_cache_put_reference(struct foo_cache* cache, uint64_t key, void* data, uint32_t initial_reference);

int foo_cache_change_data(struct foo_cache* cache, uint64_t key, void* newdata);

// release some amount of entries of the cache
// returns the number actually released
int foo_cache_release(struct foo_cache* cache, uint32_t amount);

// release some amount of entries from anyc cache that allows auto_compaction
// returns the number actually released
int foo_cache_release_any();

// return the count of items in the cache
unsigned int foo_cache_entry_count(struct foo_cache* cache);

// iterate the items in the cache through callback function
// the iteration is terminated if the callback returns negative int
// returns 0 for success, or the value that callback() returns when terminated
int foo_cache_item_iterate(struct foo_cache *cache, iteration_callback callback, void* arg);
int foo_cache_item_iterate_reference(struct foo_cache *cache, iteration_callback_reference callback, void* arg);



// 注意：如果cmd的结果是int（如创建fd），则需要用( (fd=xxx(...)) >= 0 )这样的形式
//      如果cmd的结果是指针（如stfd），则可以用stfd = xxx(....)这样的形式
#define MAKE_FD_ELASTIC(cmd, failure_message, errorcode)			\
	while(true)														\
	{																\
		if (likely(cmd)) 											\
		{															\
			break;													\
		}															\
		if (likely(errno==ENFILE || errno==EMFILE)) 				\
		{															\
			LOG_WARN("fd limit reached, trying to close some");		\
			if (likely(foo_cache_release_any() > 0))				\
			{														\
				LOG_INFO("release fd success redo");				\
				continue;											\
			}														\
			/*error release*/										\
			LOG_INFO("release fd error st sleep sometime and c1");	\
			st_sleep(1);											\
			continue;												\
		}															\
		if (errno == EADDRNOTAVAIL)									\
		{															\
			LOG_WARN("no available TCP port, trying to close some");\
			if (likely(foo_cache_release_any() > 0))				\
			{														\
				LOG_INFO("EADDRNOTAVAIL release fd error sleep ");	\
				st_sleep(5);										\
				continue;											\
			}														\
			LOG_INFO("release fd error st sleep sometime and c2");	\
			st_sleep(1);											\
			continue;												\
		}															\
		LOG_INFO(failure_message);									\
		return errorcode;											\
	}


int foo_cache_bring_to_front(void* hash, uint64_t key);

#ifdef __cplusplus
}
#endif

#endif
